﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;
using MT.Common;
using MT.Common.EfExtenstion;
using MT.Models.Entity;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Xbb.Common.Extension;

namespace MT.DAL.Infrastructure {
    public abstract class RepositoryBase<TEntity> where TEntity : class {

        protected readonly AppDbContext DataContext;
        protected readonly DbSet<TEntity> DbSet;

        protected RepositoryBase (AppDbContext dbContext) {
            this.DataContext = dbContext;
            DbSet = DataContext.Set<TEntity> ();

        }

        public async Task<int> SaveChangesAsync (AppDbContext db) {
            try {
                var result = await db.SaveChangesAsync ();
                //if (result <= 0)
                //{
                //    throw new ApplicationException("服务错误");
                //}
                return result;
            } catch (DbUpdateException exception) {

                HandleDbUpdateException (exception);
                throw new ApplicationException (exception.InnerException.Message);
                // return 0;
                //    var errorMessages =
                //        exception.InnerException
                //            .SelectMany(validationResult => validationResult.ValidationErrors)
                //            .Select(m => m.ErrorMessage);

                //var fullErrorMessage = string.Join(", ", errorMessages);
                ////记录日志
                //Log.Error(fullErrorMessage);
                //var exceptionMessage = string.Concat(exception.Message, " 验证异常消息是：", fullErrorMessage);

                //throw new DbEntityValidationException(exceptionMessage, exception.EntityValidationErrors);
            }

        }
        /// <summary>
        /// 统一处理数据库报的异常
        /// </summary>
        /// <param name="ex">DbUpdateException</param>
        public void HandleDbUpdateException (DbUpdateException ex) {
            if (ex.InnerException is SqlException sqlException) {
                switch (sqlException.Number) {
                    case 547:
                        //外键报错
                        var message = sqlException.Errors[0]?.Message;
                        break;
                    case 2601:
                    case 2627:
                        //唯一约束报错
                        var message2 = sqlException.Errors[0]?.Message;
                        break;
                    case 50000:
                        //自定义报错
                        var message3 = sqlException.Errors[0]?.Message;
                        break;
                }
            }
        }

        #region Add
        public async Task<int> AddAsync (TEntity model) {
            DbSet.Add (model);
            return await SaveChangesAsync (DataContext);
        }
        public async Task AddAsync (List<TEntity> entities) {
            await DbSet.AddRangeAsync (entities);
            await SaveChangesAsync (DataContext);
        }
      
        public async Task BulkAddAsync (List<TEntity> list) {
            var tblName = typeof (TEntity).Name;

            using (var context = new AppDbContext ()) {
                var ConnectionString = context.Database.GetDbConnection ().ConnectionString;
                await BulkInsert (ConnectionString, tblName, list);
            }

        }
        public async Task<int> UpdateRestValueAsync<T> (IQueryable<T> query, Expression<Func<T, bool>> updateValues) where T : class, new () {
            var (sql, sp) = BatchExtentions.GetSqlUpdate (query, updateValues);
            return await DataContext.Database.ExecuteSqlCommandAsync (sql, sp);
        }
        private async Task BulkInsert (string connection, string tableName, IList<TEntity> list) {
            using (var bulkCopy = new SqlBulkCopy (connection)) {
                bulkCopy.BatchSize = list.Count;
                bulkCopy.DestinationTableName = tableName;

                var table = new DataTable ();
                var props = TypeDescriptor.GetProperties (typeof (TEntity))

                    .Cast<PropertyDescriptor> ()
                    .Where (propertyInfo => propertyInfo.PropertyType.Namespace != null &&
                        propertyInfo.PropertyType.Namespace.Equals ("System"))
                    .ToArray ();

                foreach (var propertyInfo in props) {
                    bulkCopy.ColumnMappings.Add (propertyInfo.Name, propertyInfo.Name);
                    table.Columns.Add (propertyInfo.Name, Nullable.GetUnderlyingType (propertyInfo.PropertyType) ?? propertyInfo.PropertyType);
                }

                var values = new object[props.Length];
                foreach (var item in list) {
                    for (var i = 0; i < values.Length; i++) {
                        values[i] = props[i].GetValue (item);
                    }

                    table.Rows.Add (values);
                }
                await bulkCopy.WriteToServerAsync (table);
                //bulkCopy.WriteToServer(table);
            }
        }
        #endregion

        #region 删除 
        /// <summary>
        /// 根据 id 删除
        /// </summary>
        /// <param name="model">包含要删除id的对象</param>
        /// <returns></returns>
        public async Task<int> DelAsync (TEntity model) {
            DbSet.Attach (model);
            DbSet.Remove (model);
            return await SaveChangesAsync (DataContext);
        }
        public async Task<int> DelAsync (List<TEntity> entities) {
            foreach (TEntity entity in entities) {
                DbSet.Attach (entity);
            }
            DbSet.RemoveRange (entities);
            return await SaveChangesAsync (DataContext);
        }
        public async Task<int> DelByAsync (Expression<Func<TEntity, bool>> delWhere) {
            List<TEntity> listDeleting = DbSet.Where (delWhere).ToList ();
            // DbSet.Attach(u);
            if (listDeleting.Count > 0) {
                DbSet.RemoveRange (listDeleting);
                return await SaveChangesAsync (DataContext);
            }
            return 0;

        }
        #endregion

        #region  Modify
        public async Task UpsertAsync (TEntity entity, Func<TEntity, bool> insertExpression) {
            if (insertExpression (entity)) {
                await AddAsync (entity);
            } else {
                await UpdateAsync (entity);
            }
        }
        public async Task<int> UpdateAsync (TEntity entity) {

            DataContext.Update (entity);
            return await this.SaveChangesAsync (DataContext);
        }
        public async Task<int> UpdateAsync (List<TEntity> entities) {
            //await AsyncExtension.YieldConfigureAwait().ConfigureAwait(false);
            //foreach (TEntity entity in entities)
            //{
            //    DbSet.Attach(entity);
            //    DataContext.Entry<TEntity>(entity).State = EntityState.Modified;
            //}
            DataContext.UpdateRange (entities);
            return await this.SaveChangesAsync (DataContext);
        }
        public async Task<int> UpdateAsync (TEntity entity, List<string> fieldNames) {
            if (fieldNames != null && fieldNames.Count > 0) {
                DbSet.Attach (entity);
                foreach (var item in fieldNames) {
                    DataContext.Entry<TEntity> (entity).Property (item).IsModified = true;
                }
            } else {
                DataContext.Entry<TEntity> (entity).State = EntityState.Modified;
            }

            return await DataContext.SaveChangesAsync ();
        }
        public async Task<int> UpdateAsync (List<TEntity> entitys, List<string> fieldNames) {
            if (fieldNames != null && fieldNames.Count > 0) {
                foreach (var entity in entitys) {
                    // DbSet.Attach(entity);
                    //var entry = DataContext.Entry<TEntity>(entity);
                    //entry.State = EntityState.Unchanged;
                    DataContext.Set<TEntity> ().Attach (entity);
                    foreach (var item in fieldNames) {
                        DataContext.Entry<TEntity> (entity).Property (item).IsModified = true;
                    }
                }
            } else {
                foreach (var entity in entitys) {
                    DataContext.Entry<TEntity> (entity).State = EntityState.Modified;
                }

            }

            return await DataContext.SaveChangesAsync ();
        }
        /// <summary>
        /// 修改，如：
        /// TEntity u = new TEntity() { uId = 1, uLoginName = "asdfasdf" };
        /// this.Modify(u, "uLoginName");
        /// </summary>
        /// <param name="model">要修改的实体对象</param>
        /// <param name="proNames">要修改的 属性 名称</param>
        /// <returns></returns>
        public async Task<int> UpdateAsync (TEntity model, params string[] proNames) {
            EntityEntry entry = DataContext.Entry<TEntity> (model);
            entry.State = EntityState.Unchanged;
            foreach (string proName in proNames) {
                entry.Property (proName).IsModified = true;
            }
            await AsyncExtension.YieldConfigureAwait ().ConfigureAwait (false);
            return DataContext.SaveChanges ();
        }
        /// <summary>
        /// 批量修改
        /// </summary>
        /// <param name="model">要修改的实体对象</param>
        /// <param name="whereLambda">查询条件</param>
        /// <param name="modifiedProNames">要修改的 属性 名称</param>
        /// <returns></returns>
        public async Task<int> UpdateByAsync (TEntity model, Expression<Func<TEntity, bool>> whereLambda, params string[] modifiedProNames) {
            List<TEntity> listModifing = DbSet.Where (whereLambda).ToList ();

            Type t = typeof (TEntity); // model.GetType();

            List<PropertyInfo> proInfos = t.GetProperties (BindingFlags.Instance | BindingFlags.Public).ToList ();

            Dictionary<string, PropertyInfo> dictPros = new Dictionary<string, PropertyInfo> ();

            proInfos.ForEach (p => {
                if (modifiedProNames.Contains (p.Name)) {
                    dictPros.Add (p.Name, p);
                }
            });

            foreach (string proName in modifiedProNames) {

                if (dictPros.ContainsKey (proName)) {

                    PropertyInfo proInfo = dictPros[proName];

                    object newValue = proInfo.GetValue (model, null); //object newValue = model.uName;

                    foreach (TEntity usrO in listModifing) {

                        proInfo.SetValue (usrO, newValue, null);
                    }
                }
            }
            //一次性 生成sql语句到数据库执行

            return await SaveChangesAsync (DataContext);
        }
        #endregion

        #region Query
        /// <summary>
        /// 根据条件查询实体(异步)
        /// </summary>
        public async Task<TEntity> GetAsync (Expression<Func<TEntity, bool>> @where) {
            await AsyncExtension.YieldConfigureAwait ().ConfigureAwait (false);
            return DbSet.FirstOrDefault (where);
        }
        public async Task<TEntity> GetAsync<TKey> (Expression<Func<TEntity, bool>> @where, Expression<Func<TEntity, TKey>> @ordering, bool orderAsc = false) {
            await AsyncExtension.YieldConfigureAwait ().ConfigureAwait (false);
            return !orderAsc ? DbSet.OrderByDescending (@ordering).FirstOrDefault (@where) : DbSet.OrderBy (@ordering).FirstOrDefault (@where);
        }
        public async Task<IQueryable<TEntity>> GetList2Async (Expression<Func<TEntity, bool>> @where) {
            await AsyncExtension.YieldConfigureAwait ().ConfigureAwait (false);
            return DbSet.AsNoTracking ().Where (where);
        }

        /// <summary>
        /// 根据条件查询实体集合(需自行tolist,自行异步)
        /// </summary>
        public async Task<IQueryable<TEntity>> GetListAsync<TKey> (Expression<Func<TEntity, bool>> @where) {
            await AsyncExtension.YieldConfigureAwait ().ConfigureAwait (false);
            return DbSet.Where (where);
        }

        public async Task<IQueryable<TEntity>> GetListAsync<TKey> (IQueryable<TEntity> queryable) {
            await AsyncExtension.YieldConfigureAwait ().ConfigureAwait (false);
            return queryable;
        }
        public async Task<IQueryable<TEntity>> GetListAsync<TKey> (Expression<Func<TEntity, bool>> @where, Expression<Func<TEntity, TKey>> @ordering, bool orderAsc = false) {
            await AsyncExtension.YieldConfigureAwait ().ConfigureAwait (false);
            var result = DbSet.Where (where);
            if (@ordering != null) {
                result = orderAsc ? result.OrderBy (@ordering) : result.OrderByDescending (@ordering);
            }
            return result;
        }
        public async Task<IEnumerable<TEntity>> GetByListAsync<TKey> (int top, Expression<Func<TEntity, bool>> @where, Expression<Func<TEntity, TKey>> @ordering, bool orderAsc = false) {
            var result = DbSet.AsQueryable ().AsNoTracking ();
            if (@where != null)
                result = result.Where (@where);

            if (@ordering != null) {
                result = orderAsc ? result.OrderBy (@ordering) : result.OrderByDescending (@ordering);
            }
            if (top > 0) {
                result = result.Take (top);
            }
            return await result.ToListAsync ();
        }
        /// <summary>
        /// 根据传进来的实体类型查询该实体(异步)
        /// </summary>
        protected async Task<TM> GetAsync<TM> (Expression<Func<TM, bool>> @where) where TM : class {
            return await DataContext.Set<TM> ().FirstOrDefaultAsync (where);

        }
        /// <summary>
        /// 根据条件查询实体数量(异步)
        /// </summary>
        public async Task<int> CoutAsync<TM> (Expression<Func<TM, bool>> @where) where TM : class {
            try {
                //await AsyncExtension.YieldConfigureAwait().ConfigureAwait(false);
                return await DataContext.Set<TM> ().AsNoTracking ().CountAsync (where);
            } catch (Exception) {
                return -1;
            }
        }
        /// <summary>
        /// 根据条件查询实体数量
        /// </summary>
        public async Task<int> CoutAsync (Expression<Func<TEntity, bool>> @where) {
            try {
                return await DbSet.AsNoTracking ().CountAsync (where);
            } catch (Exception) {
                return 0;
            }
        }
        /// <summary>
        /// 直接获取特定一个或者多个字段的值
        /// 多个字段需要声明Model  单个字段就比较简单了 var s = testDal.GetScalar<string>(m => m.Code == "02018", m => m.Name); 
        /// 多个字段需要定义Model  var s=  testDal.GetScalar<StoreM>(m=>m.Code== "02018",m=>new StM { Name1= m.Name, Code=m.Code });
        /// 但是Model比较麻烦，可以采用dynamic
        /// <typeparam name="T"></typeparam>
        /// <param name="predicate"></param>
        /// <param name="scalar"></param>
        /// <returns></returns>
        public async Task<T> GetScalarAsync<T> (Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, T>> scalar) {
            return predicate == null ?
                await DbSet.AsQueryable ().AsNoTracking ().Select (scalar).FirstOrDefaultAsync () :
                await DbSet.AsQueryable ().AsNoTracking ().Where (predicate).Select (scalar).FirstOrDefaultAsync ();
        }

        public async Task<List<T>> GetScalarListAsync<T> (IQueryable<TEntity> queryable, Expression<Func<TEntity, T>> scalar) {
            await AsyncExtension.YieldConfigureAwait ().ConfigureAwait (false);
            var result = queryable.Select (scalar);
            return result.ToList ();
        }

        public async Task<List<T>> GetScalarListAsync<T> (Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, T>> scalar) {
            await AsyncExtension.YieldConfigureAwait ().ConfigureAwait (false);
            if (predicate == null) {
                var result = DbSet.Select (scalar);
                return result.ToList ();
            } else {
                var result = DbSet.Where (predicate).Select (scalar);
                return result.ToList ();
            }
        }
        #endregion

        #region PageQuery
        /// <summary>
        /// 分页查询 + List<TEntity> GetPagedList<TKey>
        /// <param name="pageIndex">页码</param>
        /// <param name="pageSize">页容量</param>
        /// <param name="whereLambda">条件 lambda表达式</param>
        /// <param name="orderBy">排序 lambda表达式</param>
        /// <returns></returns>
        protected async Task<List<TEntity>> GetPagedAsync<TKey> (int pageIndex, int pageSize, Expression<Func<TEntity, bool>> whereLambda, Expression<Func<TEntity, TKey>> orderBy) {
            // 分页 一定注意： Skip 之前一定要 OrderBy
            //await AsyncExtension.YieldConfigureAwait().ConfigureAwait(false);
            return await DbSet.Where (whereLambda).OrderBy (orderBy).Skip ((pageIndex - 1) * pageSize).Take (pageSize).ToListAsync ();
        }
        protected async Task<Tuple<List<TEntity>, int>> GetPagedAsync<T> (IQueryable<TEntity> queryable, int pageIndex, int pageSize) {
            var rowCount = queryable.Count ();
            var result = await queryable.Skip ((pageIndex - 1) * pageSize).Take (pageSize).ToListAsync ();
            return new Tuple<List<TEntity>, int> (result, rowCount);
        }
        protected async Task<Tuple<List<T>, int>> GetIdsPagedAsync<T> (IQueryable<TEntity> queryable, int pageIndex, int pageSize, Expression<Func<TEntity, T>> scalar) {
            var rowCount = queryable.Count ();
            var result = await queryable.Skip ((pageIndex - 1) * pageSize).Take (pageSize).Select (scalar).ToListAsync ();
            return new Tuple<List<T>, int> (result, rowCount);
        }

        /// <summary>
        /// 6.1分页查询 带输出
        /// </summary>
        protected async Task<Tuple<List<TEntity>, int>> GetPagedAsync<T> (int pageIndex, int pageSize, Expression<Func<TEntity, bool>> whereLambda, Expression<Func<TEntity, T>> orderBy, bool isAsc = true) {
            var rowCount = DbSet.Where (whereLambda).Count ();
            //1.查询分页数据
            var result = isAsc ?
                await DbSet.Where (whereLambda).OrderBy (orderBy).Skip ((pageIndex - 1) * pageSize).Take (pageSize)
                .ToListAsync () :
                await DbSet.Where (whereLambda).OrderByDescending (orderBy).Skip ((pageIndex - 1) * pageSize)
                .Take (pageSize).ToListAsync ();
            return new Tuple<List<TEntity>, int> (result, rowCount);
        }

        protected async Task<Tuple<List<T>, int>> GetIdsPagedAsync<T> (int pageIndex, int pageSize, Expression<Func<TEntity, bool>> whereLambda, Expression<Func<TEntity, T>> scalar, Expression<Func<TEntity, object>> orderBy, bool isAsc = true) {
            var rowCount = DbSet.Where (whereLambda).Count ();
            //1.查询分页数据
            var result = isAsc ?
                await DbSet.Where (whereLambda).OrderBy (orderBy).Skip ((pageIndex - 1) * pageSize).Take (pageSize)
                .Select (scalar).ToListAsync () :
                await DbSet.Where (whereLambda).OrderByDescending (orderBy).Skip ((pageIndex - 1) * pageSize)
                .Take (pageSize).Select (scalar).ToListAsync ();
            return new Tuple<List<T>, int> (result, rowCount);
        }

        protected async Task<Tuple<List<T>, int>> GetIdsPagedAsync<T> (int pageIndex, int pageSize, Expression<Func<TEntity, bool>> whereLambda, Expression<Func<TEntity, T>> scalar, Expression<Func<TEntity, T>> orderBy, Expression<Func<TEntity, T>> orderByTwo, bool isAsc = true) {
            var rowCount = DbSet.Where (whereLambda).Count ();
            //1.查询分页数据
            var result = isAsc ?
                await DbSet.Where (whereLambda).OrderBy (orderBy).ThenBy (orderByTwo)
                .Skip ((pageIndex - 1) * pageSize).Take (pageSize).Select (scalar).ToListAsync () :
                await DbSet.Where (whereLambda).OrderByDescending (orderBy).ThenByDescending (orderByTwo)
                .Skip ((pageIndex - 1) * pageSize).Take (pageSize).Select (scalar).ToListAsync ();

            return new Tuple<List<T>, int> (result, rowCount);
        }

        protected async Task<Tuple<List<TEntity>, int>> GetPagedAsync<TKey> (int pageIndex, int pageSize, Expression<Func<TEntity, bool>> whereLambda, Expression<Func<TEntity, TKey>> orderBy, Expression<Func<TEntity, TKey>> orderByTwo, bool isAsc = true) {
            var rowCount = DbSet.Where (whereLambda).Count ();
            //1.查询分页数据
            var result = isAsc ?
                await DbSet.Where (whereLambda).OrderBy (orderBy).ThenBy (orderByTwo).Skip ((pageIndex - 1) * pageSize)
                .Take (pageSize).ToListAsync () :
                await DbSet.Where (whereLambda).OrderByDescending (orderBy).ThenByDescending (orderByTwo)
                .Skip ((pageIndex - 1) * pageSize).Take (pageSize).ToListAsync ();
            return new Tuple<List<TEntity>, int> (result, rowCount);
        }
        #endregion

        /// <summary>
        /// 执行SQL查询
        /// </summary>
        /// <param name="sql">SQL语句</param>
        /// <param name="parameters">参数</param>
        /// <returns>实体对象集合</returns>
        /// <exception cref="ArgumentNullException">SQL语句为空</exception>
        public async Task<List<T>> ExecuteSqlQueryAsync<T> (string sql, params object[] parameters) where T : new () {
            if (string.IsNullOrWhiteSpace (sql)) {
                throw new ArgumentNullException (nameof (sql), @"SQL语句不可为空！");
            }
            await AsyncExtension.YieldConfigureAwait ().ConfigureAwait (false);
            var result = this.DataContext.Database.SqlQuery<T> (sql, parameters);
            return result.ToList ();
        }
        public async Task<T> ExecuteSqlFirstAsync<T> (string sql, params object[] parameters) where T : new () {
            if (string.IsNullOrWhiteSpace (sql)) {
                throw new ArgumentNullException (nameof (sql), @"SQL语句不可为空！");
            }
            await AsyncExtension.YieldConfigureAwait ().ConfigureAwait (false);

            var result = this.DataContext.Database.SqlQuery<T> (sql, parameters);
            return result == null ? default (T) : result.FirstOrDefault ();
        }
        public IQueryable<TEntity> FromSql (string sql, params object[] parameters) => DbSet.FromSql (sql, parameters);
        public void Commit (Action action) {
            var beginTransaction = DataContext.Database.BeginTransaction ();

            try {
                //调用委托
                action?.Invoke ();

                beginTransaction.Commit ();
            } catch (Exception ex) {
                beginTransaction.Rollback ();
                throw new ApplicationException (ex.Message);
            }
        }
        //public void Dispose()
        //{
        //    DataContext.Dispose();
        //    GC.SuppressFinalize(this);
        //}
    }

}